﻿using System;
using System.Linq;

namespace NNCore
{
	public class Controller
	{
		private NN nn;

		private NetXmlSerializer nxs;

		private const int netVectorSize = 2837;
		public static int NetVectorSize
		{
			get { return netVectorSize; }
		}


		public Controller(string path)
		{
			Setup(path);
		}

		public Controller(string path, double[] vector)
		{			
			Setup(path);
			SetNetAsVector(vector);
		}

		public double Run(double[] input)
		{
			if (input.Length != 64)
				throw new Exception("Ungültige Länge des Input-Arrays: " + input.Length);
            double ret = nn.Run(input)[0, 0];
            nn.ResetActivationValues();
            return ret;
		}

		public double[] GetNetVector()
		{
			int counter = 0;
			double[] netVector = new double[netVectorSize];
			foreach (Layer tmpLayer in nn.Layers)
			{
				if (tmpLayer.WeightMatrix != null)
				{
					double[] tmpVector = tmpLayer.WeightMatrix.Vector;
					for (int i = 0; i < tmpVector.Length; i++)
					{
						netVector[counter] = tmpVector[i];
						counter++;
					}
				}

				if (tmpLayer.Threshold != null)
				{
					double[] tmpThreshold = tmpLayer.Threshold;
					for (int i = 0; i < tmpThreshold.Length; i++)
					{
						netVector[counter] = tmpThreshold[i];
						counter++;
					}
				}
			}
			return netVector;
		}

		public void SetNetAsVector(double[] vector)
		{
			if (vector.Length != netVectorSize)
				throw new Exception("Ungültige Länge des Vektors (" + netVectorSize + " gefordert): " + vector.Length);

			int counter = 0;

			foreach (Layer tmpLayer in nn.Layers)
			{
				if (tmpLayer.WeightMatrix != null)
				{
					double[] tmpVector = new double[tmpLayer.WeightMatrix.Vector.Length];
					for (int i = 0; i < tmpVector.Length; i++)
					{
						tmpVector[i] = vector[counter];
						counter++;
					}
					tmpLayer.WeightMatrix.Vector = tmpVector;
				}

				if (tmpLayer.Threshold != null)
				{
					double[] tmpThreshold = new double[tmpLayer.Threshold.Length];
					for (int i = 0; i < tmpThreshold.Length; i++)
					{
						tmpThreshold[i] = vector[counter];
						counter++;
					}
					tmpLayer.Threshold = tmpThreshold;
				}
			}

		}

		public void Save()
		{
			nxs.saveNN(nn);
		}

		private void Setup(string path)
        {

            if (!path.Equals(string.Empty))
            {
                nxs = new NetXmlSerializer(path);
                if (System.IO.File.Exists(path))
                {
                    nn = nxs.loadNN();
                    return;
                }
            }

            DefaultNetLoader dl = new DefaultNetLoader();
            nn = dl.loadNN();
            
		}

		private void Test()
		{
			NetXmlSerializer nxs_test = new NetXmlSerializer("D:/nn/testibesti_compare.xml");
			DefaultNetLoader dl = new DefaultNetLoader();
			nn = dl.loadNN();
			double[] input = CreateRandomInput();

			RunAndOutput(nn, input, "Untouched dummyloaded");

			nxs.saveNN(nn);

			nn = nxs.loadNN();

			RunAndOutput(nn, input, "Safed and Reloaded");

			double[] netVector = GetNetVector();

			Console.WriteLine("Net As Vector: " + String.Join(",", netVector.Select(p => p.ToString()).ToArray()));
			SetNetAsVector(netVector);

			nxs_test.saveNN(nn);

			RunAndOutput(nn, input, "Get Set Vector");


			Console.ReadLine();
		}

		private void RunAndOutput(NN nn, double[] input, string info)
		{
			Matrix result = nn.Run(input);
			Console.WriteLine("--------------------------------");
			Console.WriteLine(info);
			Console.WriteLine("Input: " + String.Join(",", input.Select(p => p.ToString()).ToArray()));
			Console.WriteLine("Result: " + result);
			Console.WriteLine("--------------------------------");
		}

		public double[] CreateRandomInput()
		{
			double[] input = new double[64];

			for (int i = 0; i < 64; i++)
			{
				input[i] = (Math.Abs(Guid.NewGuid().GetHashCode()) % 3) - 1;
			}

			return input;
		}

        public override string ToString()
        {

            if (nn == null)
                return null;

            string result = "";
            double[] netVector = GetNetVector();
            double[] tmp;

            tmp = netVector.Take(64).ToArray();
            result += "Layer 1 Threshold: \n" + String.Join(",", tmp.Select(p => p.ToString()).ToArray());
            tmp = netVector.Skip(64).Take(64*42).ToArray();
            result += "\nLayer 2: WM\n" + String.Join(",", tmp.Select(p => p.ToString()).ToArray());
            tmp = netVector.Skip(64+64*42).Take(42).ToArray();
            result += "\nLayer 2 Threshold: \n" + String.Join(",", tmp.Select(p => p.ToString()).ToArray());
            tmp = netVector.Skip(64 + 64 * 42+42).Take(42).ToArray();
            result += "\nLayer 3: WM\n" + String.Join(",", tmp.Select(p => p.ToString()).ToArray());
            tmp = netVector.Skip(64 + 64 * 42 + 42+42).Take(1).ToArray();
            result += "\nLayer 3 Threshold: \n" + String.Join(",", tmp.Select(p => p.ToString()).ToArray());


            return result;
        }
	}
}
